package edu.cmu.javadb;

import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;

import edu.cmu.javadb.annotations.*;
import edu.cmu.javadb.exceptions.NoAttributeException;
import edu.cmu.javadb.exceptions.NotComparableException;

public class RecordInfo {	
	private Class<?> type;
	private List<IndexInfo> indexInfo = new ArrayList<IndexInfo>();
	private Map<String, Attribute> attributes = new HashMap<String, Attribute>();
	private static Map<Class<?>, RecordInfo> infoMap = new HashMap<Class<?>, RecordInfo>();
	
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(type.toString()+":");
		for (IndexInfo ii : indexInfo) {
			sb.append(ii.toString() + ", ");
		}
		return sb.toString();
	}
	
	
	public Class<?> getRecordClass() {
		return type;
	}
	
	public Attribute getAttribute(String name) throws NoAttributeException {
		if (!attributes.containsKey(name)) {
			throw new NoAttributeException();
		}
		return attributes.get(name);
	}
	
	public Attribute getAttribute(String[] path) throws NoAttributeException {
		RecordInfo rec = this;
		Attribute res = null;
		for (String p : path) {
			res = rec.getAttribute(p);
			rec = RecordInfo.getRecordInfo(res.getType());
		}
		return res;
	}
	
	/*
	 * 
	 */
	public static void RegisterRecordInfo(Class<?> cl) throws NotComparableException {
		for (Annotation ann : cl.getAnnotations()) {
		//	System.out.println(ann.annotationType().toString());
			
			if (ann instanceof Record) {
				System.out.println(ann);
				Record r = (Record) ann;
				RecordInfo ri = new RecordInfo();
				ri.type = cl;
				
				//register all the attributes
				for (Field f : cl.getDeclaredFields()) {
					try {
						Attribute a = new Attribute(f);
						ri.attributes.put(a.getName(), a);
					} catch (NoAttributeException e) {
						e.printStackTrace();
					}
				}
				
				///register all the index
				for (IndexOn in : r.value()) {
					IndexInfo ii = new IndexInfo();
					
					for (String s : in.value()) {
						try {
							Attribute att = ri.getAttribute(s);
														
							if (!Comparable.class.isAssignableFrom(att.getType()) && !att.getType().isPrimitive()) {
								throw new NotComparableException();
							}
							ii.addAttribute(att);							
						} catch (SecurityException e) {
							e.printStackTrace();
						} catch (NoAttributeException e) {
							e.printStackTrace();
						}
					}					
					ri.indexInfo.add(ii);
				}
				infoMap.put(cl, ri);
				//System.out.println("got it: " + cl.toString() + "\t" + ri.toString());
			}
		}		
	}
	
	public static RecordInfo getRecordInfo(Class<?> cl) {
		return infoMap.get(cl);
	}

	public void setIndexInfo(List<IndexInfo> indexInfo) {
		this.indexInfo = indexInfo;
	}

	public List<IndexInfo> getIndexInfo() {
		return indexInfo;
	}


	public Map<String, Attribute> getAttributes() {
		return attributes;
	}
}
